#include "TcpConnection.h"
#include "Logger.h"
#include "Channel.h"
#include "Buffer.h"
#include "EventLoop.h"

#include <functional>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <netinet/tcp.h>
#include <sys/socket.h>
#include <unistd.h>


static EventLoop* CheckLoopNull(EventLoop* loop)
{
    if(loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d mainLoop is null!\n",__FILE__,__FUNCTION__,__LINE__);
    }
    return loop;
}

TcpConnection::TcpConnection(EventLoop *loop, const std::string &name, int sockfd, const InetAddress &localAdd, const InetAddress &peerAddr)
    :loop_(CheckLoopNull(loop)),
    name_(name),
    state_(KConnected),
    socket_(new Socket(sockfd)),
    Channel_(new Channel(loop,sockfd)),
    localAddr_(localAdd),
    peerAddr_(peerAddr),
    highWaterMark_(64 * 1024 * 1024)
{
    Channel_->setReadCallback(
       std:: bind(&TcpConnection::handleRead, this, std::placeholders::_1));

    Channel_->setWriteCallback(
       std:: bind(&TcpConnection::handleWrite, this));
    
    Channel_->setCloseCallbck(
       std:: bind(&TcpConnection::handleClose, this));

    Channel_->setErrorCallback(
        std::bind(&TcpConnection::handleError, this));

    LOG_DEBUG("TcpConnection::ctor[name=%s] at fd= %d\n",name_.c_str(),sockfd);
}


void TcpConnection::send(const std::string &buf)
{
    if(state_ == KConnected)
    {
        if(loop_->isInLoopThread())
        {
            sendInLoop(buf.c_str(),buf.size());
        }
        else
        {
            loop_->runInLoop(std::bind(
                &TcpConnection::sendInLoop,
                this,
                buf.c_str(),
                buf.size()
            ));
        }
    }
}

// 发送数据
void TcpConnection::sendInLoop(const void *data, size_t len)
{
    size_t nwrote = 0;
    size_t remaining = 0;
    bool faultError = false;

    if(state_ == KDisconnected)
    {
        LOG_ERROR("disconnected,give up writing!\n");
        return;
    }

    // 表示channel第一次开始写数据，而且缓冲区没有要发送的数据
    if(!Channel_->isWriting() && outputBuffer_.readableBytes() == 0)
    {
        nwrote = ::write(Channel_->fd(),data,len);
        if(nwrote >= 0)
        {
            remaining = len - nwrote;
            if(remaining == 0 && writeCompleteCallback_)
            {
                // 既然所有的数据都发送完成，就不用在对epollout感兴趣
                loop_->queueInLoop(
                    std::bind(writeCompleteCallback_,shared_from_this())
                );
            }
        }
        else // nwrote < 0
        {
            nwrote = 0;
            if(errno != EWOULDBLOCK)
            {
                LOG_ERROR("TcpConnection::sendInLoop\n");
                if(errno == EPIPE || errno == ECONNRESET)
                {
                    faultError = true;
                }
            }
        }
    }

    // 说明len 大于了缓冲区的长度，剩下没有发送的数据就然epollout事件，调用handlewrite继续发送处理
    if (!faultError && remaining > 0)
    {
        // 目前缓存区的大小
        size_t oldlen = outputBuffer_.readableBytes();
        if (oldlen + remaining >= highWaterMark_ && oldlen < highWaterMark_ && highWaterMarkCallback_)
        {
            loop_->queueInLoop(
                std::bind(highWaterMarkCallback_,shared_from_this(),oldlen+remaining)
            );
        }

        outputBuffer_.append((char*)data + nwrote,remaining);
        if(!Channel_->isWriting())
        {
            Channel_->enableWriting();
        }
    }
}

void TcpConnection::shutdown()
{
    if(state_ == KConnected)
    {
        setState(KDisconnecting);
        loop_->runInLoop(
            std::bind(&TcpConnection::shutdownInLoop,this)
        );
    }
}

void TcpConnection::shutdownInLoop()
{
    // 说明发送缓冲区的数据已经发送完毕了
    if(!Channel_->isWriting())
    {
        socket_->shutdownWrite();
    }
}

TcpConnection::~TcpConnection()
{
    LOG_DEBUG("TcpConnection::dtor[name=%s] at fd=%d state=%d",name_.c_str(),Channel_->fd(),to_string(state_));
}

// 连接建立
void TcpConnection::connectEstablished()
{
    setState(KConnected);
    Channel_->tie(shared_from_this());
    Channel_->enableReading();  // 设置poiller中读事件就绪

    connectionCallback_(shared_from_this());
}

void TcpConnection::connectDestroyed()
{
    if(state_ == KConnected)
    {
        setState(KDisconnected);
        Channel_->disableAll(); // 将channel感兴趣的事件，从poller中移除
        connectionCallback_(shared_from_this());
    }

    Channel_->remove();
}

void TcpConnection::handleRead(Timestamp receiveTime)
{
    int saveErrno = 0;
    ssize_t n = inputBuffer_.readFd(Channel_->fd(),&saveErrno);
    if(n > 0)
    {
        messageCallback_(shared_from_this(),&inputBuffer_,receiveTime);
    }
    else if(n == 0)
    {
        handleClose();
    }
    else
    {
        errno = saveErrno;
        handleError();
    }
}

void TcpConnection::handleWrite()
{
    if(Channel_->isWriting())
    {
        int saveErrno = 0;
        ssize_t n = outputBuffer_.writeFd(Channel_->fd(),&saveErrno);
        if(n > 0)
        {
            outputBuffer_.retrieve(n);
            if(outputBuffer_.readableBytes() == 0)
            {
                Channel_->disableWriting();
                if(writeCompleteCallback_)
                {
                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
                }

            }
            else
            {
                LOG_DEBUG("TcpConnection::handleWrite\n");
            }
        }
        else 
        {
            LOG_DEBUG("Connection fd = %d is down,no more writing\n",Channel_->fd());
        }
    }
}

void TcpConnection::handleClose()
{
    LOG_DEBUG("fd=%d state=%d\n",Channel_->fd(),state_.load());
    setState(KDisconnected);
    Channel_->disableAll();

    TcpConnectionPtr connPtr(shared_from_this());
    connectionCallback_(connPtr);   // 执行连接关闭的回调
    closeCallback_(connPtr);    // 关闭连接的回调函数
}

void TcpConnection::handleError()
{
    int optval = 0;
    socklen_t optlen = sizeof(optval);
    int err = 0;
    if(::getsockopt(Channel_->fd(),SOL_SOCKET,SO_ERROR,&optval,&optlen) < 0)
    {
        err = errno;
    }
    else
    {
        err = optval;
    }
    LOG_ERROR("TcpConnection::handleError name:%s - SO_ERROR:%d\n",name_.c_str(),err);
}
